package com.lap.framework.crypto;

import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.KeySpec;
import java.util.ArrayList;
import java.util.List;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.SecretKeySpec;

/**
 *
 *
 * <pre>
 * Utility class for encryption and decryption.
 * </pre>
 *
 * @author Shuisheng Lao(劳水生)
 * @version 0.0.1
 */
final class CryptoUtil {
  private static final SecureRandom SECURE_RANDOM = new SecureRandom();

  private CryptoUtil() {}

  /**
   * Generates a random nonce of the specified length.
   *
   * @param numBytes the length of the nonce
   * @return the generated nonce
   */
  public static byte[] getRandomNonce(int numBytes) {
    byte[] nonce = new byte[numBytes];
    SECURE_RANDOM.nextBytes(nonce);
    return nonce;
  }

  /**
   * Generates an AES key from the given password and salt.
   *
   * @param password the password
   * @param salt the salt
   * @return the generated AES key
   * @throws NoSuchAlgorithmException if the algorithm is not available
   * @throws InvalidKeySpecException if the key specification is invalid
   */
  public static SecretKey getAESKeyFromPassword(char[] password, byte[] salt)
      throws NoSuchAlgorithmException, InvalidKeySpecException {
    SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
    KeySpec spec = new PBEKeySpec(password, salt, 65536, 256);
    return new SecretKeySpec(factory.generateSecret(spec).getEncoded(), "AES");
  }

  /**
   * Converts a byte array to its hexadecimal representation.
   *
   * @param bytes the byte array
   * @return the hexadecimal representation
   */
  public static String hex(byte[] bytes) {
    StringBuilder hexString = new StringBuilder();
    for (byte b : bytes) {
      hexString.append(String.format("%02x", b));
    }
    return hexString.toString();
  }

  /**
   * Converts a byte array to its hexadecimal representation with block size split.
   *
   * @param bytes the byte array
   * @param blockSize the block size
   * @return the hexadecimal representation with block size split
   */
  public static String hexWithBlockSize(byte[] bytes, int blockSize) {
    String hex = hex(bytes);
    blockSize *= 2; // one hex = 2 chars

    List<String> blocks = new ArrayList<>();
    for (int i = 0; i < hex.length(); i += blockSize) {
      blocks.add(hex.substring(i, Math.min(i + blockSize, hex.length())));
    }
    return blocks.toString();
  }
}
